"""mcRNC and ADA"""


import re
import os
import time
import types
import hashlib
import logging

import xml_tool
import linuxlib
import counter_tool

ADA_UNIT = ['CLA']
MC_UNIT = ['USPU', 'CFPU', 'CSPU', 'EIPU']

class McrncAda(linuxlib.BaseLinux):
    def __init__(self, **args):
        linuxlib.BaseLinux.__init__(self, **args)
        self.set_prompt("# ")
        self.type = self.name.split('_')[0]
        self.emil_units = {}
        if self.type == "ADA":
            self.__init_ada()
        elif self.type == "mcRNC":
            self.__init_mcrnc()
        self.checked = False
        self.nodes = []
        self.build = None
        self.zplan = "/root/zplan_new"
        self.mon_dir = "/mnt/log/mon"
        self.counter_rslt = "/mnt/log/counter"
        self.omu = {"index": 0, "log_addr": "", "phys_addr": ""}

    def __init_ada(self):
        self.flag = "QFOMU"
        self.counter_dirs = [
            "/var/opt/nokiasiemens/SS_PM9/storage/results", 
            "/var/opt/nokiasiemens/SS_ILOMU/SS_ILStatistics/transfer"
        ]
        self.emil_cond = "0000"
        self.emil_units['CLA'] = True

    def __init_mcrnc(self):
        self.flag = "QNOMU"
        self.counter_dirs = [
            "/var/opt/nokiasiemens/SS_ILOMU/SS_ILStatistics/results"
        ]
        self.emil_cond = "0001,0002,08037,08038,0C5DF,0CECA,0A3DD"
        for var in MC_UNIT:
            self.emil_units[var] = False
        self.emil_units['USPU'] = True

    def set_emil_units(self, units):
        self.emil_units = units

    def get_emil_units(self):
        return self.emil_units
    
    def check_unit_active(self, node):
        unit = node.split('-')[0]
        if self.emil_units.has_key(unit):
            return self.emil_units[unit]
        else:
            return False

    def disconnect(self):
        self.close()
        self.checked = False

    def create_localpath(self, path):
        if path and not os.path.isdir(path):
            os.makedirs(path)
            self.log('info', "Create local path " + path)
            return True
        else:
            return False

    def check(self, force=False):
        if self.connected and self.checked and not force:
            return True

        if not (self.connected or self.connect()):
            return False

        if force or not self.checked:
            self.log("notice", "Checking %s info " % self.name, set_index='rnc_check')
            kwargs = dict(head='', end='', index='rnc_check', unset_index='rnc_check')

            self.send("ls /mnt/ | grep OMU")
            if self.stdout == self.flag:
                self.log("notice", "successful", tag='g', **kwargs)
                self.checked = True
            else:
                self.log("notice", "failed", tag='g', **kwargs)
                self.log('error', "check rnc info error", record=False)
                self.checked = False

            if self.checked:
                self.log("notice", "Checking OMU info ", set_index='omu_check')
                kwargs = dict(head='', end='', index='omu_check', unset_index='omu_check')
                self.send("ilclifunit -s -t OMU")
                prog = re.compile(r'OMU-(\d)\s+(0x\d+)\s+(0x\d+)\s+WO-EX')
                infos = prog.findall(self.stdout)
                if infos:
                    self.log("notice", "successful", tag='g', **kwargs)
                    self.omu["index"] = int(infos[0][0])
                    self.omu["log_addr"] = infos[0][1]
                    self.omu["phys_addr"] = infos[0][2]
                    self.log('debug', "OMU-%d %s %s" % (self.omu["index"], 
                                                        self.omu["log_addr"], 
                                                        self.omu["phys_addr"]))
                else:
                    self.log("notice", "failed", tag='g', **kwargs)
                    self.checked = False

            if self.checked:
                self.send("cat /proc/cmdline")
                prog = re.compile(r'rel=(R_\w{4}[0-9._]+).WR')
                ver_infos = prog.findall(self.stdout)
                if ver_infos:
                    self.build = ver_infos[0]
                    self.log('info', "Build info: %s" % self.build)
                else:
                    self.log('warn', "get build info error.")

        return self.checked

    def get_nodes(self):
        nodes = []
        if self.type == "ADA":
            cmd = "fshascli --tab -a /cla*"
        else:
            cmd = "fshascli --tab -a /*PU*"
        self.send(cmd)
        prog = re.compile(r"/([A-Z]+-\d)\s+UNLOCKED")
        for foo in prog.findall(self.stdout):
            nodes.append(foo)
        uscps = []
        prog = re.compile(r"(\w+-\d)\s+\w+\s+(\w+)\s.*?/(USPU-\d)")
        for foo in nodes:
            if "USPU" in foo:
                self.send("ilclifunit -u -t USCP | grep 'WO-EX.*/%s'" % foo)
                uscps.extend(prog.findall(self.stdout))
        return nodes, uscps

    def restart(self):
        self.log('info', "Reboot %s" % self.name, end=' ')
        if self.connected:
            self.send("fshascli -rn /")
            self.log('notice', "successful", head='', tag='g')
            self.disconnect()
        else:
            self.log('notice', "failed", head='', tag='r')
            self.log('warn', "RNC not connected")

    def reset_ss7(self):
        if self.check():
            self.log('warn', "Reset SS7 signal.")
            self.send("fshascli -rn /ss7SGU")

    def catch_emil_stop(self):
        if not self.check():
            return False
        taskfile = "%s/emil_task" % self.mon_dir
        self.send("if [ -f {0} ]; then cat {0}; rm {0}; fi".format(taskfile))
        logs = []  # need keep emil log
        if self.stdout:
            for line in self.stdout.splitlines():
                logs.append(line)
            #self.notice("find %d files need to stop." % len(logs))

        self.send('ps -ef | grep "ssh.*monster"')
        prog =  re.compile('root\s+(\d+)\s+.*?ssh ([A-Za-z]+-\d) monster')

        nodes = []
        kwargs = dict(head='', end=' ', index='unit lineend')
        for taskid, node in prog.findall(self.stdout):
            if len(nodes) == 0:
                self.log('notice', "stop monster in ", set_index='unit')
            if node not in nodes:
                self.log('notice', node, **kwargs)
                nodes.append(node)
            self.send("kill -9 {0}; wait {0} 2>/dev/null".format(taskid))
            self.send("ssh %s killall -2 monster" % node)
        if nodes:
            time.sleep(2.0)

        for foo in nodes:
            if "CLA-0" == foo or "CFPU-0" == foo:
                continue
            else:
                self.send("scp {0}:{1}/* {1}/".format(foo, self.mon_dir))
        if logs:
            self.chdir(self.mon_dir)
            self.send("ls -1", cd=True)
            for line in self.stdout.splitlines():
                if line not in logs:
                    self.send("rm -f %s" % line, cd=True)
            
            self.send("ls | grep '.bin$' | wc -l", cd=True)
            if int(self.stdout) > 0 and len(logs) > 1:
                newlog = "%s_combin.bin" % re.findall(r'\d{8}_\d{6}', logs[0])[0]
                self.send("cat *.bin > %s" % newlog, cd=True)
                # delete logs
                self.send("ls | grep -v combin | sed 's/^/rm /g' | sh", cd=True)
                # compress result file
                #self.send("hwzip %s" % newlog, cd=True)
            #else:
                # compress result file
                #self.send("hwzip %s" % logs[0], cd=True)
        if nodes:
            kwargs['unset_index'] = 'unit'
            #kwargs['tag'] = 'g'
            self.log('notice', 'done', **kwargs)
        
        return True

    def catch_emil_node(self, mk, m, t, c, f, node, subnote=None, phy=None):
        """ssh node board to start monster
        mk : whether mkdir 
        m : monster string
        t : timestamp
        c : -c paremeter
        f : taskfile"""
        if mk is True:
            cmd = 'ssh {0} "mkdir -pv {1};rm -f {1}/*"'.format(node, self.mon_dir)
            self.send(cmd)

        if phy:
            self.log('debug', "catch emil log at %s(%s %s)" % (node, subnote, phy))
            logfile = "%s/%s_%s_%s.bin" % (self.mon_dir, t, node, subnote)
            cmd = "ssh %s '%s -u %s -o %s -c %s' &" % (node, m, phy, logfile, c)
        else:
            self.log('debug', "catch emil log at %s" % node)
            logfile = "%s/%s_%s.bin" % (self.mon_dir, t, node)
            cmd = "ssh %s '%s -o %s -c %s' &" % (node, m, logfile, c)
        self.send(cmd, timeout=0.5)
        self.send('echo "%s" >> %s' % (os.path.basename(logfile), f))

    def catch_emil(self):
        if not self.check():
            return False
        # check and mkdir monster path
        cmd = "if [ ! -d {0} ]; then mkdir -pv {0}; fi".format(self.mon_dir)
        self.send(cmd)

        self.catch_emil_stop()
        for var in self.emil_units.values():
            if var:
                break;
        else:
            self.log('warn', "No active unit to catch.")
            return False

        self.log('notice', "Start catch Emil at ", set_index='unit')
        kwargs = dict(head='', end=' ', index='unit lineend')
        f = "%s/emil_task" % self.mon_dir
        c = '"SR:NOT(NUM=%s)"' % self.emil_cond
        m = "monster -z 30720{0}".format(" -R" if self.type == "ADA" else "")
        t = time.strftime('%Y%m%d_%H%M%S', time.localtime())
        nodes, uscps = self.get_nodes()
        #nodes = self.get_nodes()
        sshed_nodes = []
        for foo in nodes:
            if "USPU" in foo:
                continue
            if self.check_unit_active(foo):
                self.log('notice', foo, **kwargs)
                self.catch_emil_node(foo not in sshed_nodes, m, t, c, f, foo)
                if foo not in sshed_nodes:
                    sshed_nodes.append(foo)
        if uscps:
            for uscp, phy, uspu in uscps:
                if self.check_unit_active(uspu):
                    self.log('notice', "%s(%s)" % (uspu, uscp), **kwargs)
                    self.catch_emil_node(uspu not in sshed_nodes, m, t, c, f, uspu, uscp, phy)
                    if uspu not in sshed_nodes:
                        sshed_nodes.append(uspu)

        kwargs['unset_index'] = 'unit'
        #kwargs['tag'] = 'g'
        self.log('notice', 'done', **kwargs)
        return True

    def collect_emil(self, logpath):
        if not self.check():
            return False
        self.send(r"ls -1 %s/*bin" % self.mon_dir)
        if not self.stdout:
            self.log('warn', "no emil log found.")
            return False

        self.create_localpath(logpath)
        for logname in self.stdout.splitlines():
            local_file = "%s\\%s" % (logpath, os.path.basename(logname))
            self.get(logname, local_file, "emil log", True)
            self.send("rm -f %s" % logname)
        return True

    def active_counter(self):
        if not self.check():
            return False
        self.log('info', "Start active counter")
        measplan = "MEASPLAN.XML"
        remotefile = "/mnt/%s/plan/%s" % (self.flag, measplan)
        localfile = "%s\\tools\\%s" % (os.getcwd(), measplan)
        if not os.path.isfile(localfile):
            self.log('error', "%s is not exist." % localfile)
            return False
        self.put(localfile, remotefile)

        self.log('info', "Send activate measurements plan commands.")
        dmx_cmds = [
            "*,0000,04ed,0000,00,00,0000,0d45 -b xd00,xd00,xd00,xd00,xd00",
            "*,0000,04ed,0000,00,00,0000,0d47 -b xd00"
        ]
        self.send_dmx_cmd(dmx_cmds)
        #self.notice("you should restart %s later." % self.type)

    def deactive_counter(self):
        # no used
        if not self.check():
            return False
        else:
            self.send_dmx_cmd("*,0000,04ed,0000,00,00,0000,11aa -b xb02")
            return True

    def catch_counter(self):
        if not self.check():
            return False
        # check and mkdir monster path
        cmd = "if [ ! -d {0} ]; then mkdir -pv {0}; fi".format(self.counter_rslt)
        self.send(cmd)

        # modify time
        self.send("date +%s")
        localtime = int(self.stdout)
        m = int(time.strftime("%M", time.localtime(localtime)))
        if (15 - m % 15) < 4:
            localtime += (15 - m % 15) * 60
            new_time = time.strftime("%Y-%m-%d %H:%M:10", 
                                     time.localtime(localtime))
            self.send("date -s '%s'" % new_time)
            time.sleep(5.0)

        # remove all file in Measurement cache
        cmd = "rm %s/*.*; " % self.counter_rslt
        for var in self.counter_dirs:
            cmd += "rm %s/*.*; " % var
        self.send(cmd)

        self.log('info', "Start all measurements right now")
        self.send_dmx_cmd("*,0000,04ed,0000,00,00,0000,11aa -b xb00")
        return True

    def catch_counter_stop(self):
        if not self.check():
            return False

        self.log('info', "Stop all measurements right now")
        self.send_dmx_cmd("*,0000,04ed,0000,00,00,0000,11aa -b xb01")

        self.log('notice', "wait counter file")
        self.chdir(self.counter_dirs[0])
        cmd = "ls | grep 'D00' | wc -l"
        for times in xrange(10):
            if times > 0:
                time.sleep(3.0)
            self.send(cmd, cd=True)
            if int(self.stdout) > 0:
                time.sleep(3.0)
                break
        else:
            self.log('warn', "no counter find, please check counter is active.")
            return False

        # move all counters to result folder
        for path in self.counter_dirs:
            self.send("mv %s/*.D00 %s 2> /dev/null" % (path, self.counter_rslt))

        self.chdir(self.counter_rslt)
        self.send("ls | grep D00", cd=True)
        self.log('info', "find counter file: " + ', '.join(self.stdout.splitlines()))
        self.chdir(None)
        return True

    def collect_counter(self, logpath):
        if not self.check():
            return False
        self.chdir(self.counter_rslt)

        self.send(r'ls -1 | grep ".D00$"| sed "s:^:$(pwd)/:g"', cd=True)
        if not self.stdout:
            self.log('warn', "no counter file found.")
            return False
        self.create_localpath(logpath)
        for line in self.stdout.splitlines():
            local_file = logpath + "\\" + os.path.basename(line)
            self.get(line, local_file, "counter", True)
            self.send("rm %s" % line)
            decode_rslt = counter_tool.decode_file(local_file)
            if decode_rslt:
                self.log('notice', "decode counter: " + decode_rslt)
        return True


    def collect_plan(self, logpath=None):
        if not self.check():
            return False
        if logpath is None:
            logpath = os.environ.get('TEMP')
            t = time.strftime("%Y%m%d_%H%M%S", time.localtime())
        else:
            foldername = os.path.basename(logpath)
            t = re.findall(r"\d{8}_\d{6}", foldername)[0]
        localxml = "%s\\%s_%s_%s.xml" % (logpath, self.name, self.build, t)
        if self.upload_plan(localxml):
            return localxml
        else:
            return False

    def check_plan_tool(self):
        local_zplan = "%s\\tools\\%s" % (os.getcwd(), os.path.basename(self.zplan))
        
        if not os.path.isfile(local_zplan):
            self.log('error', "local zplan tool(%s) not found." % local_zplan)
            return False

        self.send("md5sum %s | awk '{print $1}'" % self.zplan)
        if self.stderr or (self.stdout != md5sum(local_zplan)):
            self.put(local_zplan, self.zplan, "zplan")
            self.send("chmod +x %s" % self.zplan)
        return True


    def download_active_plan(self, xml, trace):
        if not self.check() or not xml or not os.path.isfile(xml):
            return False
        self.log('info' ,"start download RNW Plan %s." % xml)
        remotefile = "/mnt/%s/plan/RNWPLAND.XML" % self.flag
        self.put(xml, remotefile, "RNW Plan")
        msg = "download and active plan"
        cmd = "%s -i -d -a" % self.zplan
        if self.check_plan_tool() and self.send_plan_cmd(msg, cmd, timeout=180):
            logger = logging.getLogger('rnw_plan')
            logger.info("%s - %s - %s" % (self.name, self.build, xml))
            return True
        else:
            return False

    def upload_plan(self, xml):
        if not self.check() or not self.check_plan_tool():
            return False
        if not self.send_plan_cmd("upload plan from rnc", "%s -u" % self.zplan, timeout=100):
            return False
        self.create_localpath(os.path.dirname(os.path.abspath(xml)))
        remotefile = "/mnt/%s/plan/RNWPLANU.XML" % self.flag
        self.get(remotefile, xml, "RNW plan", True)

        return xml_tool.tidy_plan(xml)

    def send_plan_cmd(self, comment, cmd, timeout):
        self.log('info', comment, end=' ')
        self.send(cmd, timeout=timeout)
        if "Error" in self.stdout:
            err_msg = []
            for line in self.stdout.splitlines():
                if "***" in line and "Error" not in line:
                    err_msg.append(line[5:-5])
            self.log('notice', "failed", head='', tag='r')
            self.log('error', '\n'.join(err_msg))
            return False
        else:
            self.log('notice', "successful", head='', tag='g')
            return True

    def send_dmx_cmd(self, commands):
        bash_file = "/tmp/dmx.sh"
        cmd = "echo -e 'export UNIT_NAME=OMU\n"
        cmd += "export GET_CONFIG=/etc/LibgenConfig_OMU.ini\n"
        cmd += "export LIBGEN_USE_PHYS_ADDR=%s\n" % self.omu["phys_addr"]
        cmd += "export LIBGEN_USE_LOG_ADDR=%s\n" % self.omu["log_addr"]

        if type(commands) == types.ListType:
            for command in commands:
                cmd += "dmxsend -- -h %s\n" % command
        else:
            cmd += "dmxsend -- -h %s\n" % commands
        cmd += "' > %s" % bash_file

        self.send(cmd)
        self.send("date +%s; bash " + bash_file)

def md5sum(f):
    if os.path.isfile(f):
        m = hashlib.md5()
        with open(f, 'rb') as fh:
            while True:
                data = fh.read(8192)
                if not data:
                    break
                m.update(data)
        return m.hexdigest()
    else:
        return 0
